package com.derbysoft.synxisadapter.commons.repository.impl;

import com.derbysoft.core.CriteriaCreator;
import com.derbysoft.core.Limit;
import com.derbysoft.core.LimitedList;
import com.derbysoft.core.Sorter;
import com.derbysoft.synxisadapter.commons.model.Hotel;
import com.derbysoft.synxisadapter.commons.repository.HotelRepository;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

public class HotelRepositoryImpl extends BaseRepositorySupport implements HotelRepository {

    @Override
    public void save(Hotel hotel) {
        super.dataAccessor.saveOrUpdate(hotel);
    }

    @Override
    public void delete(Hotel hotel) {
        super.dataAccessor.delete(hotel);
    }

    @Override
    public void disableAll(List<Hotel> hotels) {
        for (Hotel hotel : hotels) {
            hotel.setStatus(Hotel.Status.DISABLED);
            super.dataAccessor.saveOrUpdate(hotel);
        }
    }

    @Override
    public Hotel findByPassport(String passport) {
        return findByPassport(passport, null);
    }

    @Override
    public Hotel findByPassport(final String passport, final Hotel.Status status) {
        return dataAccessor.findUnique(Hotel.class, new CriteriaCreator() {
            @Override
            public Criteria createCriteria(Session session) {
                Criteria criteria = session.createCriteria(Hotel.class);
                criteria.add(Restrictions.eq("passport", passport));
                if (status != null) {
                    criteria.add(Restrictions.eq("status", status));
                }

                return criteria;
            }
        });
    }

    @Override
    @Transactional(readOnly = false)
    public void saveAll(List<Hotel> hotels) {
        for (Hotel hotel : hotels) {
            super.dataAccessor.save(hotel);
        }
    }

    @Override
    public Hotel findById(String id) {
        return dataAccessor.get(Hotel.class, id);
    }

    @Override
    public LimitedList<Hotel> findByCondition(final HotelCondition condition, Limit limit) {
        return dataAccessor.findByCriteria(Hotel.class, new CriteriaCreator() {
            @Override
            public Criteria createCriteria(Session session) {
                Criteria criteria = session.createCriteria(Hotel.class);
                if (condition != null) {
                    populateCriteria(criteria, condition);
                }
                return criteria;
            }
        }, limit, new Sorter().desc("id"));
    }

    @Transactional
    @Override
    public void create(String id, String passport, String name, String providerPassport, String providerName) {
        if (findById(id) != null) {
            throw new IllegalArgumentException(String.format("Id [%s] already in use.", id));
        }

        if (findByPassport(passport, null) != null) {
            throw new IllegalArgumentException(String.format("Passport [%s] already in use.", passport));
        }

        Hotel hotel = new Hotel();

        hotel.setId(id);
        hotel.setPassport(passport);
        hotel.setName(name);
        hotel.setProviderPassport(providerPassport);
        hotel.setProviderName(providerName);
        hotel.setStatus(Hotel.Status.ENABLED);

        dataAccessor.save(hotel);
    }

    @Transactional
    @Override
    public void update(String id, String passport, String name, String providerPassport, String providerName, Hotel.Status status) {
        Hotel hotel = findById(id);
        if (hotel == null) {
            throw new IllegalArgumentException(String.format("Id [%s] not found.", id));
        }

        Hotel hotelByPassport = findByPassport(passport, null);
        if (hotelByPassport != null) {
            throw new IllegalArgumentException(String.format("Passport [%s] already in use.", passport));
        }

        hotel.setPassport(passport);
        hotel.setName(name);
        hotel.setProviderPassport(providerPassport);
        hotel.setProviderName(providerName);
        hotel.setStatus(status);

        dataAccessor.update(hotel);
    }


    private void populateCriteria(Criteria criteria, HotelCondition condition) {
        if (condition.getPassport() != null) {
            criteria.add(Restrictions.ilike("passport", condition.getPassport(), MatchMode.ANYWHERE));
        }

        if (condition.getName() != null) {
            criteria.add(Restrictions.ilike("name", condition.getName(), MatchMode.ANYWHERE));
        }

        if (condition.getId() != null) {
            criteria.add(Restrictions.eq("id", condition.getId()));
        }

        if (condition.getStatus() != null) {
            criteria.add(Restrictions.eq("status", condition.getStatus()));
        }
    }
}
